﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Rappelz.GameServer.Network;
using Rappelz.GameServer.Network.GameClient;

namespace Rappelz.GameServer
{
    public class WorldLocationManager
    {
        private static WorldLocationManager m_instance = null;

        internal static WorldLocationManager Instance
        {
            get
            {
                if (m_instance == null)
                    m_instance = new WorldLocationManager();
                return WorldLocationManager.m_instance;
            }
        }

// UserDefinedType: WorldLocationManager
// BaseClass      :   ArSchedulerObject, offset = 0x0
// VTable         :     , Type:  *
// Enum           :     AR_OBJECT_PRIORITY, Type: int
// Function       :     public void ArSchedulerObject(const struct ArSchedulerObject &)
// Function       :     public void ArSchedulerObject::ArSchedulerObject()
// Function       :     public unsigned int ArSchedulerObject::GetHandle()
// Function       :     public void ArSchedulerObject::onProcess(int)
// Function       :     public bool IsEnable()
// Function       :     public void Disable()
// Function       :     public bool IsDeleteRequested()
// Function       :     public volatile enum ArSchedulerObject::AR_OBJECT_PRIORITY GetPriority()
// Function       :     public volatile unsigned int GetPriorityQueueIndex()
// Function       :     public volatile int GetPendingPriority()
// Function       :     public volatile int GetFinalPriority()
// Function       :     public void EnterSpinLock()
// Function       :     public void LeaveSpinLock()
// Function       :     public volatile __int64 GetLastProcTime()
// Function       :     public volatile unsigned int GetLastProcTick()
// Data           :     this+0x8, Member, Type: volatile bool, bIsDeleteRequested
// Data           :     this+0x10, Member, Type: volatile __int64, last_proc_time
// Data           :     this+0x18, Member, Type: volatile unsigned int, last_proc_tick
// Data           :     this+0x1C, Member, Type: volatile enum ArSchedulerObject::AR_OBJECT_PRIORITY, pending_priority
// Data           :     this+0x20, Member, Type: volatile int, pending_thread_index
// Data           :     this+0x24, Member, Type: volatile unsigned int, pending_priority_queue_index
// Data           :     this+0x28, Member, Type: volatile enum ArSchedulerObject::AR_OBJECT_PRIORITY, priority
// Data           :     this+0x2C, Member, Type: volatile int, thread_index
// Data           :     this+0x30, Member, Type: volatile unsigned int, priority_queue_index
// Data           :     this+0x34, Member, Type: volatile int, lock
// Function       :     protected bool ArSchedulerObject::ProcDelete()
// Function       :     protected bool ArSchedulerObject::IsDeleteable()
// Function       :     protected void ArSchedulerObject::~ArSchedulerObject()
// Data           :     this+0x38, Member, Type: volatile bool, m_bIsEnable
// Function       :     public struct ArSchedulerObject & operator=(const struct ArSchedulerObject &)
// Function       :     public void __local_vftable_ctor_closure()
// Function       :     protected void * __vecDelDtor(unsigned int)
// 

        public bool Init()
        {
            return true;
        }

        public bool DeInit()
        {
            return true;
        }

        public void onProcess(int nThreadIdx)
        {

        }
        
        public WorldLocation AddToLocation(uint idx, Player player)
        {
            if (player.m_WorldLocation != null)
                RemoveFromLocation(player);
            
            lock(this)
            {
                foreach (WorldLocation wl in m_vWorldLocation)
                {
                    if(wl.idx == idx)
                    {
                        wl.m_vIncludeClient.Add(player);
                        PacketOut p = new PacketOut((ushort)ClientOutPackets.WeatherInfo);
                        p.WriteUInt32(idx);
                        p.WriteUInt16(wl.current_weather);
                        player.Connection.SendTCP(p);
                        return wl;
                    }
                }
            }
            return null;
        }

        public bool RemoveFromLocation(Player player)
        {
            lock(this)
            {
                foreach (WorldLocation wl in m_vWorldLocation)
                {
                    if(ReferenceEquals(wl, player.m_WorldLocation))
                    {
                        wl.m_vIncludeClient.Remove(player);
                        return true;
                    }
                }
            }
            return false;
        }

        public bool SendWeatherInfo(uint idx, Player player)
        {
            lock(this)
            {
                foreach (WorldLocation wl in m_vWorldLocation)
                {
                    if(wl.idx == idx)
                    {
                        PacketOut p = new PacketOut((ushort)ClientOutPackets.WeatherInfo);
                        p.WriteUInt32(idx);
                        p.WriteUInt16(wl.current_weather);
                        player.Connection.SendTCP(p);
                        return true;
                    }
                }
            }
            return false;
        }
        
        public int GetShovelableItem(uint idx)
        {
            lock(this)
            {
                foreach (WorldLocation wl in m_vWorldLocation)
                {
                    if(wl.idx == idx)
                    {
                        return wl.shovelable_item;
                    }
                }
            }
            return 0;
        }

        public uint GetShovelableMonster(uint idx)
        {
            if(!m_hsMonsterID.ContainsKey(idx))
                return 0;

            foreach (uint id in m_hsMonsterID[idx])
            {
                MonsterBase mb = GameContent.GetMonsterInfo(id);
                if(mb.fight_type == 0 || mb.fight_type == 1 || mb.fight_type == 2)
                    return id;
            }
            return 0;
        }

        public void RegisterWorldLocation(uint idx, byte location_type, uint time_id, uint weather_id, byte ratio, uint weather_change_time, int shovelable_item)
        {
            foreach (WorldLocation wl in m_vWorldLocation)
            {
                if(wl.idx == idx)
                {
                    wl.weather_ratio[weather_id,time_id] = ratio;
                    wl.shovelable_item = shovelable_item;
                    return;
                }
            }

            WorldLocation nwl = new WorldLocation();
            nwl.idx = idx;
            nwl.location_type = location_type;
            nwl.weather_ratio[weather_id, time_id] = ratio;
            nwl.weather_change_time = weather_change_time;
            nwl.shovelable_item = shovelable_item;

            m_vWorldLocation.Add(nwl);
        }

        public void RegisterMonsterLocation(uint idx, uint monster_id)
        {
            List<uint> ml;

            if (m_hsMonsterID.ContainsKey(idx))
            {
                ml = m_hsMonsterID[idx];
            }
            else
            {
                ml = new List<uint>();
                m_hsMonsterID.Add(idx, ml);
            }

            foreach(uint id in ml)
            {
                if(id == monster_id)
                    return;
            }
            ml.Add(monster_id);
        }

        public List<WorldLocation> m_vWorldLocation = new List<WorldLocation>();                    // 0x40
        public Dictionary<uint, List<uint>> m_hsMonsterID = new Dictionary<uint, List<uint>>();         // 0x50

    }
}
